package model.doe;

public class FocusatorInLine extends AbstractDoe {
	private double r, f, wl, d, angle, doeStep, cos;
	private double k0;
	private double r0;
	private double d0;
	private double f0;
	private double psi0;
	private boolean addLen;
	private double max1;
	private double min1;

	public FocusatorInLine(double r, double f, double wl, double d, double angle, double doeStep, boolean addLen) {
		this.r = r;
		this.f = f;
		this.wl = wl;
		this.d = d;
		this.angle = angle;
		this.doeStep = doeStep;
		this.addLen = addLen;
		super.widthPointsNumber = (int) (2*r / doeStep);
		super.heightPointsNumber = (int) (2*r / doeStep);
		this.d = d / 2;
		this.cos = Math.cos(this.angle * Math.PI / 180);
		
		otre_init( wl/1000, r , d , f); 
	}
	
	private double calculate (double u, double v){
		if (addLen) {
			return otre01(u, v);
		} else {
			return otre11(u, v);
		}
	}

	void otre_init(double wl, double r,	double d, double f) {

		// ************* Parameter initialization
		// *************************************

		int N = 128;

		double h = r / N;
		double res;
		int i, j;

		k0 = 2 * Math.PI / wl;

		r0 = r;

		d0 = d;

		f0 = f;

		psi0 = 0;

		if (!addLen) {

			max1 = -otre1(0, 0);

			for (i = 0; i < N; i++) {
				res = -otre1(i * h, 0);

				if (max1 < res)
					max1 = res;

			}

			psi0 = max1;

			min1 = otre1(0, 0);

			max1 = otre1(0, 0);

			for (i = 0; i < N; i++) {
				res = otre1(i * h, 0);

				if (max1 < res)
					max1 = res;

				if (min1 > res)
					min1 = res;

			}
		}

		else {

			max1 = -otre0(0, 0);

			for (i = 0; i < N; i++) {

				for (j = 0; j < N; j++)

				{
					res = -otre0(i * h, j * h);

					if (max1 < res)
						max1 = res;

				}
			}

			psi0 = max1;

			min1 = otre0(0, 0);

			max1 = otre0(0, 0);

			for (i = 0; i < N; i++) {

				for (j = 0; j < N; j++)

				{
					res = otre0(i * h, j * h);

					if (max1 < res)
						max1 = res;

					if (min1 > res)
						min1 = res;

				}
			}
		}
		maximumValue = max1;
		minValue = min1;
	}

	double otre1(double u, double v) {


		// *********** Phase function when focusator as addition to lens
		// ************

		double psi;

		psi = psi0 + 3 * d0 * k0 / (2 * r0 * r0 * r0 * f0) *

		(r0 * r0 * u * u / 2 - u * u * u * u / 12);

		return psi;
	}

	double otre11(double u, double v) {
		if (Math.sqrt(u * cos * u * cos + v * v) > r0)
			return 0;

		return otre1(u * cos, 0);
	}

	// __________________________________________________________________________

	double otre0(double u, double v) {

		// ********** Phase function when focusator contain lens function
		// ************

		double psi;

		psi = psi0 - k0 / (2 * f0) * (u * u + v * v) + 3 * d0 * k0 / (2 * r0 * r0 * r0 * f0) *

		(r0 * r0 * u * u / 2 - u * u * u * u / 12);

		return psi;
	}

	double otre01(double u, double v) {
		if (Math.sqrt(u * cos * u * cos + v * v) > r0)
			return 0;

		return otre0(u * cos, v);
	}

	@Override
	public double run(int i, int j) {
		return calculate(doeStep * i, doeStep * j);
	}
}
